package com.ruoyi.web.controller.lucky;

import com.alibaba.fastjson.JSON;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.system.domain.SnDrawConfig;
import com.ruoyi.system.domain.SnUserLib;
import com.ruoyi.system.service.ISnDrawConfigService;
import com.ruoyi.system.service.ISnUserLibService;
import com.ruoyi.thd.domain.*;
import com.ruoyi.thd.enums.EnumErrorResult;
import com.ruoyi.thd.enums.EnumResult;
import com.ruoyi.web.config.LuckConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Controller
@RequestMapping("/lottery/lucky")
public class LuckyController {

    /**
     * 彩民前端显示默认查询数
     */
    private final static int DEFAULT_USERS_SHOW_COUNT = 1000;

    @Autowired
    LuckConfig luckConfig;
    private Logger LOGGER = LoggerFactory.getLogger("draw_info");
    @Autowired
    private ISnUserLibService snUserLibService;
    @Autowired
    private ISnDrawConfigService snDrawConfigService;

    /**
     * 获取当前配置的抽奖配置信息
     *
     * @return
     */
    @GetMapping("/config")
    @ResponseBody
    @CrossOrigin(origins = "*")
    public DrawResult termInfo() {
        List<SnDrawConfig> drawConfigs = snDrawConfigService.getCurrentConfig();
        if (drawConfigs.size() == 0) {
            return new DrawResult(EnumResult.FAILURE.getCode(),
                    new ErrorMsg(EnumErrorResult.ERROR_UN_SET_TERMNO));
        }
        SnDrawConfig snDrawConfig = drawConfigs.stream().findFirst().get();
        TermInfo termInfo = new TermInfo();
        termInfo.setTermNo(snDrawConfig.getTermNo());
        termInfo.setFirstTitle(snDrawConfig.getFirstTitle());
        termInfo.setSecondTitle(snDrawConfig.getSecondTitle());
        termInfo.setMaxLucky(snDrawConfig.getMaxLucky());
        termInfo.setStartDay(snDrawConfig.getStartDay());
        termInfo.setFinishDay(snDrawConfig.getFinishDay());
        termInfo.setDrawDate(snDrawConfig.getDrawDate());
        return new DrawResult(EnumResult.SUCCESS.getCode(), termInfo);
    }

    /**
     * 获取当前参与抽奖活动的彩民信息
     * 前端根据显示效果，查询部分彩民至前端展示
     *
     * @param limit
     * @return
     */
    @GetMapping("/users/{limit}")
    @ResponseBody
    @CrossOrigin(origins = "*")
    public DrawResult getUsers(@PathVariable(name = "limit") Integer limit) {
        List<SnDrawConfig> drawConfigs = snDrawConfigService.getCurrentConfig();
        if (drawConfigs.size() == 0) {
            return new DrawResult(EnumResult.FAILURE.getCode(),
                    new ErrorMsg(EnumErrorResult.ERROR_UN_SET_TERMNO));
        }
        SnDrawConfig snDrawConfig = drawConfigs.stream().findFirst().get();

        SnUserLib object = new SnUserLib();
        int size = Optional.ofNullable(limit).orElse(DEFAULT_USERS_SHOW_COUNT);
        object.setLucky(0);
        object.setTermNo(snDrawConfig.getTermNo());
        object.setParams(new HashMap<String, Object>() {{
            put("limit", size);
        }});
        List<SnUserLib> snUserLibs = snUserLibService.selectSnUserLibList(object);
        List<UserInfo> userInfos = snUserLibs.stream().map($ ->
                new UserInfo($.getUserName(), $.getIdCard(), $.getMobile())
        ).collect(Collectors.toList());
        //随机打乱
        Collections.shuffle(userInfos);
        //彩民总数量
        SnUserLib entity = new SnUserLib();
        entity.setTermNo(snDrawConfig.getTermNo());
        int totalUser = snUserLibService.distantSnUserLibCount(entity);//用户去重
        int totalTicket = snUserLibService.selectSnUserLibCount(entity);//全部票数
        LuckyInfo luckyInfo = new LuckyInfo();
        luckyInfo.setList(userInfos);
        luckyInfo.setTotal(totalTicket);
        luckyInfo.setCount(totalUser);
        return new DrawResult(EnumResult.SUCCESS.getCode(), luckyInfo);
    }

    /**
     * 抽奖
     * 1、获取当前期配置
     * 2、判断已经抽出的人数是否超出配置
     * 3、随机抽取其中一个彩民作为中奖彩民
     * 4、判断是否已经中过奖，不可多中时，重新再抽一个彩民。最多抽奖次数限制。
     *
     * @return
     */
    @GetMapping("/draw")
    @ResponseBody
    @CrossOrigin(origins = "*")
    public DrawResult draw() {
        List<SnDrawConfig> drawConfigs = snDrawConfigService.getCurrentConfig();
        if (drawConfigs.size() == 0) {
            return new DrawResult(EnumResult.FAILURE.getCode(),
                    new ErrorMsg(EnumErrorResult.ERROR_UN_SET_TERMNO));
        }
        SnDrawConfig snDrawConfig = drawConfigs.stream().findFirst().get();

        int currentLucky = snUserLibService.getCurrentLucky();
        if (currentLucky >= snDrawConfig.getMaxLucky()) {
            return new DrawResult(EnumResult.FAILURE.getCode(),
                    new ErrorMsg(EnumErrorResult.ERROR_MAX_DRAW_COUNT));
        }
        //抽奖中奖序号累加
        int seq = currentLucky + 1;
        //期号
        String termNo = snDrawConfig.getTermNo();

        //抽取幸运彩民，其中限制当期同一彩民不可多中
        long start = System.currentTimeMillis();
        SnUserLib lucky = randomLuckyUser(termNo, seq);
        long end = System.currentTimeMillis();
        LOGGER.info("[" + termNo + "期]本次抽奖耗时[" + (end - start) + "ms]");
        //判断是否抽中彩民
        if (!Optional.ofNullable(lucky).isPresent()) {
            return new DrawResult(EnumResult.FAILURE.getCode(),
                    new ErrorMsg(EnumErrorResult.ERROR_UN_LUCKY_USER));
        }
        lucky.setLucky(1);
        lucky.setSeq(seq);
        lucky.setAwardTime(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, new Date()));
        //更改当前抽中彩民为中奖彩民
        int res = snUserLibService.updateSnUserLib(lucky);
        //返回中奖彩民信息
        if (res > 0) {
            UserInfo userInfo = new UserInfo();
            userInfo.setName(lucky.getUserName());
            userInfo.setIdCard(lucky.getIdCard());
            userInfo.setMobile(lucky.getMobile());
            userInfo.setSeq(seq);
            userInfo.setLast((seq == snDrawConfig.getMaxLucky()) ? (true) : (false));
            return new DrawResult(EnumResult.SUCCESS.getCode(), userInfo);
        } else {
            return new DrawResult(EnumResult.FAILURE.getCode(), new ErrorMsg(EnumErrorResult.ERROR_DRAW_FAILURE));
        }
    }


    /**
     * DB随机抽取一个彩民
     *
     * @param termNo
     * @param seq
     * @return
     */
    private SnUserLib randomLuckyUser(String termNo, int seq) {
        SnUserLib obj = new SnUserLib();
        obj.setTermNo(termNo);
        obj.setLucky(0);//未中奖标志
        //当前已经中过奖的彩民
        List<SnUserLib> luckyUsers = snUserLibService.getCurrentLuckyUsers();
        LOGGER.info("[" + termNo + "]期已经中奖的彩民数" + luckyUsers.size() + " | " + JSON.toJSONString(luckyUsers));
        //最大抽奖次数
        int DRAW_MAX_COUNT = luckConfig.getLuckyDrawMaxCount();

        //同期同一彩民是否可多中
        if (!luckConfig.getLuckyDrawRepeat()) {//不可多中
            //是否继续
            boolean goOn = true;
            //抽取次数
            int drawCount = 1;
            //
            SnUserLib lucky = null;
            //判断是否继续抽
            while (goOn && drawCount <= DRAW_MAX_COUNT) {
                SnUserLib randomUser = snUserLibService.randomOneSnUserLib(obj);
                if (Optional.ofNullable(randomUser).isPresent()) {
                    int depateCount = luckyUsers.stream().filter(s -> s.getIdCard().equals(randomUser.getIdCard())
                                    && s.getUserName().equals(randomUser.getUserName())
                                    && s.getMobile().equals(randomUser.getMobile()))
                            .collect(Collectors.toList()).size();//检查当前抽中彩民是否已中过奖
                    if (depateCount == 0) {
                        lucky = new SnUserLib();
                        lucky.setMobile(randomUser.getMobile());
                        lucky.setUserName(randomUser.getUserName());
                        lucky.setIdCard(randomUser.getIdCard());
                        lucky.setId(randomUser.getId());
                        //不再继续抽取
                        goOn = false;
                        LOGGER.info("[" + termNo + "]期第" + seq + "位幸运彩民第" + drawCount + "次抽奖抽取成功，彩民信息:" + JSON.toJSON(lucky));
                    } else { //继续
                        goOn = true;
                        LOGGER.info("[" + termNo + "]期第" + seq + "位幸运彩民第" + drawCount + "次抽奖抽取失败，彩民重复中奖，继续抽取。彩民信息" + JSON.toJSON(randomUser));
                    }
                } else {
                    //继续
                    goOn = true;
                    LOGGER.info("[" + termNo + "]期第" + seq + "位幸运彩民第" + drawCount + "次抽奖抽取失败，未随机到未中奖彩民，继续抽取。");
                }
                drawCount = drawCount + 1;
            }
            LOGGER.info("开始抽取[" + termNo + "]期第" + seq + "位幸运彩民，抽取结果:" + ((Optional.ofNullable(lucky).isPresent()) ? ("成功") : ("失败")));
            return lucky;
        } else {
            SnUserLib randomUser = snUserLibService.randomOneSnUserLib(obj);
            if (!Optional.ofNullable(randomUser).isPresent()) {
                LOGGER.info("[" + termNo + "]期随机抽取1个未中奖彩民失败，无未中奖彩民");
                return null;
            }
            //
            SnUserLib lucky = null;
            lucky = new SnUserLib();
            lucky.setMobile(randomUser.getMobile());
            lucky.setUserName(randomUser.getUserName());
            lucky.setIdCard(randomUser.getIdCard());
            lucky.setId(randomUser.getId());
            LOGGER.info("抽取[" + termNo + "]期第" + seq + "位幸运彩民成功。彩民信息:" + JSON.toJSONString(lucky));
            return lucky;
        }
    }

    /**
     * 获取当前全部的中奖彩民列表
     *
     * @return
     */
    @GetMapping("/result")
    @ResponseBody
    @CrossOrigin(origins = "*")
    public DrawResult result() {
        SnUserLib object = new SnUserLib();
        object.setLucky(1);
        List<SnUserLib> snUserLibs = snUserLibService.selectSnUserLibList(object);
        List<UserInfo> userInfos = snUserLibs.stream().map($ ->
                new UserInfo($.getSeq(), $.getUserName(), $.getIdCard(), $.getMobile())
        ).collect(Collectors.toList());
        Collections.sort(userInfos, Comparator.comparing(UserInfo::getSeq));
        // userInfos.stream().sorted((Comparator.comparing(UserInfo::getSeq)));

        LuckyInfo luckyInfo = new LuckyInfo();
        luckyInfo.setList(userInfos);
        luckyInfo.setTotal(userInfos.size());
        luckyInfo.setCount(0);
        return new DrawResult(EnumResult.SUCCESS.getCode(), luckyInfo);
    }
}
